Conversation
This was referenced Aug 17, 2026
toommz
marked this pull request as ready for review
August 17, 2026 20:08
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Context
The Quotes feature shipped in the API (REST endpoints) but was never documented in the spec. Webhooks come from lago-api#6134. Both are now merged on
main, and every payload here has been re-verified against it field by field.Order forms and orders are deliberately out of scope; they get their own branches.
Changes
7 endpoints under a new
quotestag, all gated on the premiumorder_formsfeature flag:GET /quotesstatus[],order_type[],number[],owner_id[],external_customer_id[],from_date,to_dateGET /quotes/{lago_id}ownersGET /quotes/{lago_id}/versionsGET /quote_versions/{lago_id}contentandbilling_itemsPOST /quote_versions/{lago_id}/approveexpires_atfor the generated order formPOST /quote_versions/{lago_id}/voidPOST /quote_versions/{lago_id}/clone3 webhooks:
quote.created,quote.approved,quote.voided.Notes for review
billing_itemsis documented in full, from the JSON schemas inQuoteVersions::Validators::*::Schema. Its keys are camelCase, unlike the rest of the API, which the schema description calls out. Required-ness follows the update schema (what a persisted draft satisfies), with descriptions flagging the keys that only become mandatory at approval.QuoteVersions::DealExpirationfolds everyplans[].payload.endDateand every wallet-credit / recurring-ruleexpirationAtinto one boundary, and refuses a date landing on or after it, compared by date. It folded the version'send_datetoo until #6182 removed that column, so aone_offquote is now never bounded. The rule is described in prose rather than by its error code, matching the rest of the spec — no resource description names one.share_tokenis intentionally omitted, per the comment inQuoteVersionSerializer.versionkey rather thancurrent_version.QuoteBaseObjectwas extracted to share the quote fields between the two shapes.quote.voidedfires for every void path, so the description points atversion.void_reasonto tell them apart.redocly lintandspectral lintpass. The 5 newarray-params-pluralwarnings are unavoidable: the API's filter params really are singular (Array.wrap(params[:status])), and there is precedent (status[],coupon_code[],account_type[]).quotestag has noexternalDocsyet, aslago-dochas noapi-reference/quotespages.Review fixes applied
From the OpenAPI Guardian sweep, both verified against merged
mainbefore changing anything:One-off quotes are not unbounded.Applied, then superseded:#6182removedquote_version.end_datefrom the fold, so aone_offquote is unbounded after all. The current wording says that; see the section below.QuoteOrderTypeEnumno longer nameslago_subscription_idas an ambient field. The enum is reused byOrderObject, which never returns it, so the bullet now attributes the field to the quote.Now also covers lago-api#6182 + #6185
Both are merged — #6185 into #6182's branch, then #6182 squash-merged to
mainas1b1c24a. Re-verified: the REST-visible surface at that commit is identical to the branches this was written from.start_dateandend_dateare gone fromQuoteVersionObject, replaced bybilling_entity_code. #6182 drops both columns and their serializer keys; the billing items become the single source of truth for the deal term. Breaking for anyone reading those two keys, and they also leave the threequote.*webhook payloads, which embed this object.billing_entity_codeis resolved, not stored — the version's own entity, then the amended subscription's for asubscription_amendment, then the customer's, elsenull. An amendment cannot name one itself; approving a version that does is rejected.end_date— it is the earliest planendDateand wallet / recurring-ruleexpirationAt. So aone_offquote is genuinely unbounded, which is why the MF-1 fix above is struck through.overrides.amountCurrencyon plan and coupon billing items (#6185), enum'd to the currency list. The figure is restated, not converted — 150000 USD becomes 150000 EUR.payload.startDateis no longer required and has no fallback; a plan naming none starts when the order executes.422instead of a 500, on approve, void and clone.QuoteVersions::UpdateServicealso refuses a currency change on an amendment, but that path is GraphQL-only, so it is not documented here.